package main

import (
	"fmt"
	"net"
	"strconv"
	"time"
)

const (
	_NTP_HOST = "0.pool.ntp.org"
)

func GetDateTimeAny(t *time.Time) (dStr, tStr string) {
	dStr += strconv.FormatInt(int64(t.Year()), 10) + "-" + strconv.FormatInt(int64(t.Month()), 10) + "-" + strconv.FormatInt(int64(t.Day()), 10)
	tStr += strconv.FormatInt(int64(t.Hour()), 10) + ":" + strconv.FormatInt(int64(t.Minute()), 10) + ":" + strconv.FormatInt(int64(t.Second()), 10)

	return dStr, tStr
}

func SntpNow(l *time.Location) (*time.Time, error) {
	raddr, err := net.ResolveUDPAddr("udp", _NTP_HOST+":123")
	if err != nil {
		return nil, err
	}

	data := make([]byte, 48)
	data[0] = 3<<3 | 3

	con, err := net.DialUDP("udp", nil, raddr)
	if err != nil {
		return nil, err
	}

	defer con.Close()

	_, err = con.Write(data)
	if err != nil {
		return nil, err
	}

	con.SetDeadline(time.Now().Add(5 * time.Second))

	_, err = con.Read(data)
	if err != nil {
		return nil, err
	}

	var sec, frac uint64
	sec = uint64(data[43]) | uint64(data[42])<<8 | uint64(data[41])<<16 |
		uint64(data[40])<<24
	frac = uint64(data[47]) | uint64(data[46])<<8 | uint64(data[45])<<16 |
		uint64(data[44])<<24

	nsec := sec * 1e9
	nsec += (frac * 1e9) >> 32

	t := time.Date(1900, 1, 1, 0, 0, 0, 0, l).
		Add(time.Duration(nsec)).Local()

	return &t, nil
}

func printNtpTime(l *time.Location) {
	if l == nil {
		return
	}

	for i := 0; i < 10; i++ {
		t, err := SntpNow(l)
		if err == nil && t != nil {
			fmt.Printf("System time: %v\n", time.Now())
			fmt.Printf("Network time: %v\n", t)
			dstr, tstr := GetDateTimeAny(t)
			fmt.Printf("%s\n", dstr)
			fmt.Printf("%s\n\n", tstr)
			break
		}
	}
}

func main() {
	/*
		fmt.Printf("Time UTC\n")
		printNtpTime(time.UTC)
	*/

	l := time.FixedZone("UTC", 0)
	fmt.Printf("\nUTC\n")
	printNtpTime(l)
	l = nil

	//l, _ = time.LoadLocation("Asia/Shanghai")
	l = time.FixedZone("Asia/Shanghai", 8)
	fmt.Printf("\nShanghai\n")
	printNtpTime(l)
}
